Chuyển tới nội dung chính

Bai14_Generic

Mục tiêu bài học

  • Hiểu được Generic là gì và vì sao nên dùng
  • Biết cách khai báo hàm và class với Generic
  • Biết sử dụng Generic để tái sử dụng code với kiểu dữ liệu linh hoạt
  • Biết kết hợp Generic với Interface, Constraint và Default Type

Nội dung chính

1. Generic là gì?

  • Generic là một cách để viết code có thể làm việc với nhiều kiểu dữ liệu, nhưng vẫn đảm bảo kiểu an toàn (type-safe).
  • Thay vì dùng any (mất kiểm soát), Generic cho phép giữ lại thông tin kiểu.
function identity<T>(arg: T): T {
return arg;
}

console.log(identity<string>("Hello"));
console.log(identity<number>(123));

T là kiểu dữ liệu được truyền vào tại thời điểm gọi hàm.


2. Hàm Generic – cơ bản

function getFirstElement<T>(arr: T[]): T {
return arr[0];
}

const num = getFirstElement<number>([1, 2, 3]);
const str = getFirstElement<string>(["a", "b", "c"]);

3. Generic với nhiều tham số kiểu

function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}

const result = pair<string, number>("tuổi", 25); // ["tuổi", 25]

4. Generic với Interface

interface ApiResponse<T> {
data: T;
status: number;
}

const userResponse: ApiResponse<{ name: string; age: number }> = {
data: { name: "Phú", age: 22 },
status: 200,
};

5. Generic Constraint (ràng buộc)

function logLength<T extends { length: number }>(item: T): void {
console.log(item.length);
}

logLength("hello"); // ✅
logLength([1, 2, 3]); // ✅
// logLength(123); ❌ lỗi vì number không có thuộc tính length

6. Generic Class

class Storage<T> {
private items: T[] = [];

add(item: T): void {
this.items.push(item);
}

getAll(): T[] {
return this.items;
}
}

const numberStorage = new Storage<number>();
numberStorage.add(1);
numberStorage.add(2);

const stringStorage = new Storage<string>();
stringStorage.add("A");

7. Default Type trong Generic

function makeList<T = string>(): T[] {
return [];
}

const defaultList = makeList(); // string[]
const numberList = makeList<number>(); // number[]

Bài tập

Bài 1 – Hàm wrapValue

Viết một hàm generic wrapValue nhận vào giá trị bất kỳ và trả về object { value: ... }.


Bài 2 – mergeObject<T, U>

Tạo hàm mergeObject<T, U> kết hợp 2 object thành 1 object mới.


Bài 3 – Lọc mảng theo kiểu

Viết hàm filterByType<T>(arr: T[], type: string): T[] để lọc phần tử theo typeof.


Bài 4 – Class Box<T>

Tạo class Box<T> có phương thức add(), getAll(), remove(), có thể dùng cho string, number, object.


Bài 5 – Gọi API với Generic

Tạo interface ApiResponse<T> như ví dụ. Tạo một hàm getUserData(): ApiResponse<User> để mô phỏng gọi API trả về user.